/*
 * @Author: wuqingchun
 * @Date: 2024-10-09 15:34:04
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-10-11 19:33:44
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <getopt.h>
#include <arpa/inet.h>
#include <signal.h>

#include "netperf.h"

#include "netperf_udp.h"
#include "netperf_udp_server.h"

NETPERF_PARAM_S g_stNetperfParam = {};

CNetperfUDPClient* g_pobjUDPClient = nullptr;

static void __main_HandleSignal(int iSigNo)
{
    if (g_pobjUDPClient != nullptr)
    {
        printf("\n");
        g_pobjUDPClient->ShowStatistics();

        exit(EXIT_SUCCESS);
    }
}


static void __main_ShowUsage(const char* pcErrMsg)
{
    if (pcErrMsg != nullptr)
    {
        printf("%s\n", pcErrMsg);
    }
    
    printf("Usage: netperf [-h] [-m mode] [-p protocol] [-H hostname] [-P port]\n"
           "               [-w timeout] [-i interval]\n");
    printf("\t-m --mode             runing mode[client|discard|echo]\n");
    printf("\t-H --host             server hostname\n");
    printf("\t-P --port             server port\n");
    printf("\t-p --protocol         base protocol [tcp|udp]\n");
    printf("\t-h --help             this help text\n");
    printf("\t-i --interval         interval(us)\n");
    printf("\t-w --wait-timeout     wait timeout(us)\n");

    exit(EXIT_SUCCESS);
}

static void __main_RunAsUDPClient()
{
    CNetperfUDPClient objUDPClient(g_stNetperfParam);
    g_pobjUDPClient = &objUDPClient;

    signal(SIGINT, __main_HandleSignal);
    
    objUDPClient.Init();

    objUDPClient.Run();
}


static  void __main_RunAsUDPServer()
{
    CNetperfUDPServer objUDPServer(g_stNetperfParam);

    objUDPServer.Init();
    objUDPServer.Run();
}


static void __main_ParseConf(int argc, char** argv, NETPERF_PARAM_S& refParam)
{
    char* pcHostName = nullptr;
    uint16_t usPort = 8888;
    
    refParam.enProto = NETPERF_PROTO_TCP;
    refParam.enMode = NETPERF_MODE_SERVER_DISCARD;
    refParam.ulPktLen = 1024;
    refParam.uiInterval = 1000000;
    refParam.uiTimeout = 10000;

    int option_index = 0;

    static struct option long_options[] = {
        {"mode",     required_argument, 0, 'm'},
        {"host",     required_argument, 0, 'H'},
        {"port",    required_argument, 0, 'P'},
        {"protocol",    required_argument, 0, 'p'},
        {"packsize",    required_argument, 0, 's'},
        {"interval",    required_argument, 0, 'i'},
        {"wait",    required_argument, 0, 'w'},
        {"help", no_argument, 0, 'h'},
        {0,         0,                 0,  0 }
    };

    while (true)
    {
        int iCode = getopt_long(argc, argv, "hm:p:H:P:s:w:i:", long_options, &option_index);
        if (iCode == -1)
        {
            // 所有的命令行选项解析完成
            break;
        }

        switch (iCode)
        {
        case 'h':
        {
            __main_ShowUsage(nullptr);
            break;
        }
        case 'H':
        {
            pcHostName = optarg;
            break;
        }
        case 'i':
        {
            refParam.uiInterval = atoi(optarg);
            break;
        }
        case 'm':
        {
            if (strcmp(optarg, "client") == 0)
            {
                refParam.enMode = NETPERF_MODE_CLIENT;
            }
            else if (strcmp(optarg, "discard") == 0)
            {
                refParam.enMode = NETPERF_MODE_SERVER_DISCARD;
            }
            else if (strcmp(optarg, "echo") == 0)
            {
                refParam.enMode = NETPERF_MODE_SERVER_ECHO;
            }
            else
            {
                __main_ShowUsage("bad mode");
            }

            break;
        }
        case 'p':
        {
            if (strcmp(optarg, "tcp") == 0)
            {
                refParam.enProto = NETPERF_PROTO_TCP;
            }
            else if (strcmp(optarg, "udp") == 0)
            {
                refParam.enProto = NETPERF_PROTO_UDP;
            }
            else
            {
                __main_ShowUsage("bad protocol");
            }
            
            break;
        }
        case 'P':
        {
            int32_t iPort = atoi(optarg);
            if (iPort == 0 || iPort > 65535)
            {
                __main_ShowUsage("Invalid server port");
            }

            usPort = iPort;

            break;
        }
        case 's':
        {
            refParam.ulPktLen = atoi(optarg);
            break;
        }
        case 'w':
        {
            refParam.uiTimeout = atoi(optarg);
            break;
        }
        default:
            break;
        }
    }
    
    if (refParam.enMode == NETPERF_MODE_CLIENT)
    {
        if (pcHostName == nullptr || usPort == 0)
        {
            __main_ShowUsage("Need server hostname and port");
        }
    }

    if (pcHostName != nullptr)
    {
        if (inet_pton(AF_INET, pcHostName, &refParam.stAddr.sin_addr) != 1)
        {
            __main_ShowUsage("Invalid server hostname");
        }

        (void)strcpy(refParam.szHost, pcHostName);
    }
    else
    {
        (void)strcpy(refParam.szHost, "0.0.0.0");
    }

    refParam.usPort = usPort;
    refParam.stAddr.sin_port = htons(usPort);
    refParam.stAddr.sin_family = AF_INET;
}

int main(int argc, char** argv)
{
    __main_ParseConf(argc, argv, g_stNetperfParam);

    if (g_stNetperfParam.enProto == NETPERF_PROTO_TCP)
    {
        if (g_stNetperfParam.enMode == NETPERF_MODE_CLIENT)
        {
            
        }
        else
        {

        }
    }
    else
    {
        if (g_stNetperfParam.enMode == NETPERF_MODE_CLIENT)
        {
            __main_RunAsUDPClient();
        }
        else
        {
            __main_RunAsUDPServer();
        }
    }
    
    return 0;
}
